#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "shared.h" 
#include "flit.h"
#include "flit_queue.h"
#include "inj_ejt.h"
#include "power.h"

/* Mod1: 07/13/2010
 * 1. Introduce inj_flag1 and inj_flag2 to allow for injection rate > 1
 */

void inj_flit_stage(void)
{
  flit_t *flit_ptr;
  int node, chn, dnode, dx, dy; 
  bool inj_flag1, inj_flag2;

  if(num_inj_flits >= NUM_FLT_TO_INJ) 
    return;

  for(node = 0;node < NUM_NODES;node ++)
  {
    switch(TRA_TYPE)
    {
      case BERN: /* Bernoulli */
        if(INJ_RATE <= 1) 
        { 
          inj_flag1 = ((double) rand()/(double) RAND_MAX < INJ_RATE)?true:false;
          inj_flag2 = false; 
        }
        else
        {
          inj_flag1 = true; 
          inj_flag2 = ((double) rand()/(double) RAND_MAX < INJ_RATE - 1)?true:false;
        }
        break; 
      case POIS: /* Poisson */ 
        inj_flag1 = false; /* not supported now */
        break; 
      default: /* default to Bernoulli process */
        if(INJ_RATE <= 1) 
        {
          inj_flag1 = ((double) rand()/(double) RAND_MAX < INJ_RATE)?true:false;
          inj_flag2 = false;
        } 
        else
        {
          inj_flag1 = true; 
          inj_flag2 = ((double) rand()/(double) RAND_MAX < INJ_RATE - 1)?true:false;
        }
        break;
    }

    if(inj_flag1)
    {
      switch(TRA_PATTERN)
      { 
        case UNIF: //uniform
          do 
          {
            dnode = rand()%NUM_CHN;
          }while(dnode == node);  
          break;
        case HOTS: //hotspot
          dnode = 0;
          break;
        case TRAN: //transpose
          dx = node % 8;
          dy = node / 8;
          dnode = dx * 8 + dy;
          break;
        default:  //default to uniform 
          do 
          {
            dnode = rand()%NUM_CHN;
          }while(dnode == node);  
          break; 
      }

      if(node != dnode && inj_queues[node][dnode].num_flits < INJ_BUF_DEPTH)
      { 
        flit_ptr = flit_alloc(); 
        if(flit_ptr == NULL)
        { 
          printf("[%d] inj_flit_stage(): Fail to allocate flit\n", SRC_CLK); 
          exit(1); 
        }

        flit_ptr->flit_num = num_inj_flits;
        flit_ptr->inj_tm   = SRC_CLK; 
        flit_ptr->snode    = node; 
        flit_ptr->dnode    = dnode;
        num_inj_flits ++;  

        if(QOS)  //With QoS support
        { 
          if(curr_res[dnode][node] > 0)
          { 
            flit_ptr->type = READY;
            curr_res[dnode][node] --;
          }
          else
            flit_ptr->type = PENDING;
        }
        else 
          flit_ptr->type = READY;
        
        if(VERBOSE)
        {
          printf("[%d|%d|I] flit num:%d snode:%d dnode:%d\n",  
                 SRC_CLK, node, flit_ptr->flit_num, node, dnode);
        }
        send(&inj_queues[node][dnode], &flit_ptr); //Put the flit in the buffer
      }
    } 
    if(inj_flag2)
    {
      switch(TRA_PATTERN)
      { 
        case UNIF: //uniform
          do 
          {
            dnode = rand()%NUM_CHN;
          }while(dnode == node);  
          break;
        case HOTS: //hotspot
          dnode = 0;
          break;
        case TRAN: //transpose
          dx = node % 8;
          dy = node / 8;
          dnode = dx * 8 + dy;
          break;
        default:  //default to uniform 
          do 
          {
            dnode = rand()%NUM_CHN;
          }while(dnode == node);  
          break; 
      }

      if(node != dnode && inj_queues[node][dnode].num_flits < INJ_BUF_DEPTH)
      { 
        flit_ptr = flit_alloc(); 
        if(flit_ptr == NULL)
        { 
          printf("[%d] inj_flit_stage(): Fail to allocate flit\n", SRC_CLK); 
          exit(1); 
        }

        flit_ptr->flit_num = num_inj_flits;
        flit_ptr->inj_tm   = SRC_CLK; 
        flit_ptr->snode    = node; 
        flit_ptr->dnode    = dnode;
        num_inj_flits ++;  

        if(QOS)  //With QoS support
        { 
          if(curr_res[dnode][node] > 0)
          { 
            flit_ptr->type = READY;
            curr_res[dnode][node] --;
          }
          else
            flit_ptr->type = PENDING;
        }
        else 
          flit_ptr->type = READY;
        
        if(VERBOSE)
        {
          printf("[%d|%d|I] flit num:%d snode:%d dnode:%d\n",  
                 SRC_CLK, node, flit_ptr->flit_num, node, dnode);
        }
        send(&inj_queues[node][dnode], &flit_ptr); //Put the flit in the buffer
      }
    } 
  }
}  
 
unsigned int prev_flit_lat = 0; 

void ejt_flit_stage(void)
{
  flit_t *flit_ptr; 
  int dnode, pri; 
  double flit_lat;
  unsigned int jitter;  
  
  for(dnode = 0;dnode < NUM_CHN;dnode ++)
  {
    /* First eject from ejection queue */
    if(ejt_queues[dnode].num_flits > 0)
    {
      flit_ptr = recv(&ejt_queues[dnode]);
      pri      = node_pri_map[flit_ptr->snode][dnode];  
      
      flit_lat =  
        SRC_CLK - flit_ptr->inj_tm - (CHN_PHASE * pri);
      total_flit_latency += flit_lat;
      
      if(prev_flit_lat != 0)
      { 
        jitter = abs((int)flit_lat - prev_flit_lat);
        tot_jitter += jitter;
        tot_jitter_var += (jitter - 7)^2; 
        if(jitter < 1000)  
          bin_jitter[jitter/10] ++;
        else 
          bin_jitter[99] ++; 
        num_jitter ++;
      } 
      prev_flit_lat = (int)flit_lat;  
 
      if(VERBOSE)
      { 
        printf("[%d|%d|E] flit num:%d snode:%d dnode:%d latency:%f\n", 
                SRC_CLK, dnode, flit_ptr->flit_num, flit_ptr->snode, dnode, 
                flit_lat);
      } 
      num_ejt_flits ++;
      node_throughputs[flit_ptr->snode] ++;  
      flit_free(&flit_ptr); 
    } 
    /* Then take from delay tap to the ejection queue */ 
    if(flt_dly_tap[dnode][DLY_TAP_PT] != NULL)
    { 
      flit_ptr = flt_dly_tap[dnode][DLY_TAP_PT];
      /* Energy: 1. O/E conversion of 256-bit data */
      dyn_e_power += PD_OE * 512;
  
      flt_dly_tap[dnode][DLY_TAP_PT] = NULL; 
      
      if(VERBOSE)
      { 
        printf("[%d|%d|A] flit num:%d snode:%d dnode:%d\n", 
                SRC_CLK, dnode, flit_ptr->flit_num, flit_ptr->snode, dnode);
      }   
      send(&ejt_queues[dnode], &flit_ptr); 
    }
  }
}  
 
